#define changemodepin 2
#define stoppin  5

#define latchpin 9 
#define enablepin 10
#define datapin 11
#define clockpin 13

#define iRows 1
#define iCols 12
#define totalLeds iRows * iCols
#define maxMode 4

int iLed[totalLeds][3] = {0};

char iInternalState = 0;
boolean bPressChangeMode = false;
boolean bPressStop = false;
boolean bIsStopped = false;
char iMode = 0;//maxMode-1;
float fTimer = 0;

void setup() {

  pinMode(datapin, OUTPUT);
  pinMode(latchpin, OUTPUT);
  pinMode(enablepin, OUTPUT);
  pinMode(clockpin, OUTPUT);
  
  pinMode(changemodepin, INPUT);
  pinMode(stoppin, INPUT);  

  SPCR = (1<<SPE)|(1<<MSTR)|(0<<SPR1)|(0<<SPR0);

  digitalWrite(latchpin, LOW);
  digitalWrite(enablepin, LOW);
  
  //Serial.begin(57600);

}


void loop() {
  
  bPressChangeMode = (digitalRead(changemodepin))==HIGH;
  bPressStop = (digitalRead(stoppin))==HIGH;
  
  switch(iInternalState) {
    case 0:
      if (bPressChangeMode) {
        iInternalState = 1;
        iMode++;
        if (iMode >= maxMode) iMode = 0; 
        background(0,0,0);
        plot(iMode % iCols, 100, 100, 100);
      } else if (bPressStop) {
        iInternalState = 2;
        bIsStopped = !bIsStopped;
      } else {
        if (!bIsStopped) {
          switch(iMode){
            case 0:
              perlinRGB(0.01);          
              break;            
            case 1:
              perlinDampenBorder(0.01);          
              break;                                    
            case 2:
              perlinDampenMiddle(0.004);          
              break;          
            case 3:
              perlinDoubleSpiral(0.02);          
              break;                             
          }   
        }
      }
      break;
    case 1:
      if (!bPressChangeMode) {
        iInternalState = 0;
      }    
      break;
    case 2:
      if (!bPressStop) {
        iInternalState = 0;
      }    
      break;      
  }

  SB_WriteLEDData();
  delay(20);  
}



/*-----------------------------------------------------------------*/
float perlin_noise_2d(int x, int y) {
  long n=(long)x+(long)y*57;
  n = (n<<13)^ n;
  return 1.0 - (((n *(n * n * 15731L + 789221L) + 1376312589L)  & 0x7fffffff) / 1073741824.0);    
}

float perlin_map(float x, float in_min, float in_max, float out_min, float out_max){
  return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min;
}

float perlin_scale(float x, float startval, float endval){
  //valido porque x vale [0,1]
  return x * (endval - startval) + startval;  
}

float perlin_function(float x, float y){
  int fx = floor(x);
  int fy = floor(y);
  float s,t,u,v;
  s=perlin_noise_2d(fx,fy);
  t=perlin_noise_2d(fx+1,fy);
  u=perlin_noise_2d(fx,fy+1);
  v=perlin_noise_2d(fx+1,fy+1);
  float inter1 = perlin_scale(x-fx,s,t);
  float inter2 = perlin_scale(x-fx,u,v);  
  return perlin_scale(y-fy,inter1,inter2);  
}

void perlinRGB(float delta){
  for (char x=0; x<iCols;x++){
    float pr = perlin_function(x,fTimer);
    float pg = perlin_function(x+12,fTimer);      
    float pb = perlin_function(x+24,fTimer);            
    int r= perlin_dampen_function(pr,0.65,1000);
    int g= perlin_dampen_function(pg,0.35,1000);      
    int b= perlin_dampen_function(pb,0.15,800);                  
    plot(x,r,g,b);      
  }
  fTimer += delta;
}

void perlinDampenBorder(float delta){
  for (char x=0; x<iCols;x++){
    float pr = perlin_function(fTimer+x*0.25,0);
    float pg = perlin_function(-fTimer+x*0.25,0);
    float pb = perlin_function(x*0.25,fTimer);
    int r = perlin_limit_function(pr,0,1000);
    int g = perlin_limit_function(pg,0,1000);
    int b = perlin_limit_function(pb,0,800);
    plot(x,r,g,b);      
  }
  fTimer += delta;
}

void perlinDampenMiddle(float delta){
  for (char x=0; x<iCols;x++){
    float pr = perlin_function(x*0.25,1+fTimer);
    float pg = perlin_function(x*0.25,0-fTimer);
    float pb = perlin_function(x*0.25,fTimer);
    int r = perlin_dampen_function(pr,0.1,1000);
    int g = perlin_dampen_function(pg,0.1,1000);
    int b = perlin_dampen_function(pb,0.1,800);      
    plot(x,r,g,b);      
  }
  fTimer += delta;
}

void perlinDoubleSpiral(float delta){
  for (char i=0; i<=5; i++) {
    float pr = perlin_function(i*0.5+fTimer,1);    
    float pg = perlin_function(i*0.5+fTimer,3);        
    float pb = perlin_function(i*0.5+fTimer,7);            
    iLed[i][0] = iLed[11-i][0] = perlin_dampen_function(pr,0.7,1000);      
    iLed[i][1] = iLed[11-i][1] = perlin_dampen_function(pg,0.7,1000);      
    iLed[i][2] = iLed[11-i][2] = perlin_dampen_function(pb,0.7,800);          
  }
  fTimer += delta;
}

int perlin_limit_function(float pv, float threshold, int maxlight){
  return pv > threshold ? (int)perlin_map(pv,threshold,1,0,maxlight) : 0;
}

int perlin_dampen_function(float pv, float threshold, int maxlight){
  if (pv<-threshold || pv>threshold) return 0;
  if (pv>=-threshold && pv<0) 
    return (int)perlin_map(pv,-threshold,0,0,maxlight);   
  else 
    return (int)perlin_map(pv,0,threshold,maxlight,0);   
}
/*-----------------------------------------------------------------*/


void SB_SendPacket(int SB_CommandMode, int r, int g, int b) {

  if (SB_CommandMode == B01) r =  g = b = 127;

  SPDR = SB_CommandMode << 6 | b>>4;
  while(!(SPSR & (1<<SPIF)));

  SPDR = b<<4 | r>>6;
  while(!(SPSR & (1<<SPIF)));

  SPDR = r << 2 | g>>8;
  while(!(SPSR & (1<<SPIF)));

  SPDR = g;
  while(!(SPSR & (1<<SPIF)));

}


void SB_WriteLEDData() {

  for (int f=0; f<totalLeds; f++) SB_SendPacket(B00, iLed[f][0], iLed[f][1], iLed[f][2]);
  delayMicroseconds(15);
  digitalWrite(latchpin,HIGH);
  delayMicroseconds(15);
  digitalWrite(latchpin,LOW);

  for (int f=0; f< totalLeds; f++) SB_SendPacket(B01, 0, 0, 0);
  delayMicroseconds(15);
  digitalWrite(latchpin,HIGH);
  delayMicroseconds(15);
  digitalWrite(latchpin,LOW);

}

void plot(int x, int r, int g, int b){
  iLed[x][0] = r;
  iLed[x][1] = g;
  iLed[x][2] = b;  
}

void background(int r, int g, int b){
  for (int f=0; f<totalLeds; f++){
    iLed[f][0] = r;
    iLed[f][1] = g;
    iLed[f][2] = b;                       
  }       
}



